#ifndef STREAM_H
#define STREAM_H

#include <vector>

#include "BaseTypes.h"
#include "MidiFile.h"
#include "nlohmann/json.hpp"

using namespace nlohmann;

constexpr char SET_TEMPO = 0;
constexpr char NOTE_ON = 1;
constexpr char NOTE_OFF = 2;

typedef unsigned char Byte;

typedef struct NoteData {
	Byte pitch;
	Byte velocity;
}NoteData;

typedef union CombinedData {
	size_t tempo;
	NoteData note_data;
}CombinedData;

typedef struct CombinedMessage {
	size_t current_tick;
	CombinedData data;
	char type;
}CombinedMessage;

typedef struct Message {
	size_t start_frame_index;
	size_t end_frame_index;
	Byte velocity;
	Byte pitch;
}Message;

typedef struct MetaMessage {
	size_t frame_index;
	size_t tick_index;
	size_t tempo;
}MetaMessage;

class _Stream {
public:
	std::string name;
	_Stream() {};
	_Stream(smf::MidiEventList& note_track, double frame_rate, size_t tpq, json& note_sizes);
	
protected:
	size_t _note_frames;
	size_t _tpq;
	static std::vector<Message> _get_message_list(std::vector<MetaMessage>& meta_track, smf::MidiEventList& note_track, double frame_rate, size_t tpq, size_t note_frames);
	static std::vector<MetaMessage> _get_meta_message_list(smf::MidiEventList& meta_track, double frame_rate, size_t tpq);
	static size_t _get_track_frames_num(std::vector<MetaMessage>& meta_track, smf::MidiEventList& note_track, double frame_rate, size_t tpq);

private:
	static bool _compare_tick(CombinedMessage& a, CombinedMessage& b);
	static std::string _get_name(smf::MidiEventList& note_track);
	static double _tick_to_second(size_t tick, size_t tempo, size_t tpq);
};

class NoteStream :public _Stream {
public:
	Mat notes;
	size_t pitch_min;
	size_t pitch_range;
	size_t notes_num;
	NoteStream();
	NoteStream(smf::MidiEventList& meta_track, smf::MidiEventList& note_track, double frame_rate, size_t tpq, json& note_sizes);
	void align(size_t begin_frames);
	NoteStream copy();

private:
	static size_t _get_pitch_min(Mat& data);
	static size_t _get_pitch_range(Mat& data, size_t pitch_min);
	static Mat _get_stream_data(smf::MidiEventList& meta_track, smf::MidiEventList& note_track, double frame_rate, size_t tpq, size_t note_frames);
};

class VelocityStream :public _Stream {
public:
	size_t length;
	Mat velocity;
	VelocityStream();
	VelocityStream(smf::MidiEventList& meta_track, smf::MidiEventList& note_track, double frame_rate, double blur_sigma, size_t tpq, json& note_sizes);
	void align(size_t total_frame_num, size_t begin_frames, size_t end_frames);
	void clear();
	VelocityStream copy();
	void gaussian_blur();
	double get(size_t frame_index);

private:
	double _blur_sigma;
	static Mat _get_stream_data(smf::MidiEventList& meta_track, smf::MidiEventList& note_track, double frame_rate, size_t tpq, size_t note_frames);
};

#endif
